package cn.mayday.algorithms.year2021.month1.A普通业务处理;

import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: TODO
 * @author: mayday
 * @date: 2021/5/13 21:23
 * @version: 1.0
 */
public class Dahe2 {

    /*
     问题：如果你的系统登陆接口在被刷，需要要建立一个防刷系统。
     规则：根据客户端ip，30分钟之内，只能请求30次登陆请求，如果某个ip超过这个限制，则这个ip将被锁定30分钟。
    */

    public static void main(String[] args) {

    }

    private static final int MAX_LOGIN_COUNT = 30;

    private static final int TIME_WINDOW_MILLIS = 30 * 60 * 1000;

    private volatile static Map<String, List<Long>> TIME_MAP = new ConcurrentHashMap<>();

    /**
     * 判断指定IP在30分钟之内是否连续登陆超过30次
     * 通过一个保存首次登陆的时间，超过限制次数时比较最早的是否超过30分钟
     *
     * @param clientIp 指定IP
     * @return 校验结果
     */
    public static boolean checkAttack(String clientIp) {
        // 获取当前时间
        long nowTime = System.currentTimeMillis();
        // 根据队列id，取出对应的限流队列，若没有则创建
        Stack<Long> stack = (Stack<Long>) TIME_MAP.computeIfAbsent(clientIp, k -> new Stack<Long>());

        // 如果队列还没满，则允许通过，并添加当前时间戳到队列开始位置
        if (stack.size() < MAX_LOGIN_COUNT) {
            stack.push(nowTime);
            return true;
        }

        // 队列已满（达到限制次数），则获取队列中最早添加的时间戳
        Long farTime = stack.peek();
        // 用当前时间戳 减去 最早添加的时间戳
        if (nowTime - farTime <= TIME_WINDOW_MILLIS) {
            // 若结果小于等于timeWindow，则说明在timeWindow内，通过的次数大于count
            // 不允许通过
            return false;
        } else {
            // 若结果大于timeWindow，则说明在timeWindow内，通过的次数小于等于count
            // 允许通过，并删除最早添加的时间戳，将当前时间添加到队列开始位置
            stack.pop();
            stack.push(nowTime);
            return true;
        }
    }
}
